package hyl.android.redux;


import android.util.Log;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import hyl.android.redux.action.Action;
import hyl.android.redux.reducer.Reducer;
import hyl.android.redux.state.State;

class CoreStore<A extends Action, S extends State> extends Store<A, S> {

    private static final int LIST_INITIAL_CAPACITY = 50;

    private final List<Subscriber>   subscribers;
    private       Reducer<A, S>      reducer;
    private final AtomicBoolean      isReducing;
    private       Middleware<A, S>[] mMiddlewares;

    private S currentState;

    CoreStore(S initialState, Reducer<A, S> reducer, Middleware<A, S>... middlewares) {
        this.mMiddlewares = middlewares;
        this.currentState = initialState;
        this.reducer = reducer;
        this.subscribers = new ArrayList<>(LIST_INITIAL_CAPACITY);
        this.isReducing = new AtomicBoolean(false);
    }

    @Override
    public void subscribe(Subscriber subscriber) {
        Log.i("Subscription", "Subscription add " + subscriber);
        subscribers.add(subscriber);
        //return Subscription.create(subscribers, subscriber);
    }

    @Override
    public void unsubscribe(Subscriber subscriber) {
        boolean remove = subscribers.remove(subscriber);
        Log.i("Subscription", "Subscription remove " + subscriber + " " + remove);
    }

    @Override
    public S getState() {
        return currentState;
    }

    @Override
    public void dispatch(A action) {
        checkState(!isReducing.get(), "Can not dispatch an action when an other action is being processed");

        isReducing.set(true);
        currentState = reduce(action, currentState);
        isReducing.set(false);
        dispatchMiddleware(action);
        notifyStateChanged();
    }

    private void dispatchMiddleware(A action) {
        for (int i = mMiddlewares.length - 1; i >= 0; i--) {
            final Middleware<A, S> mw = mMiddlewares[i];
            mw.dispatch(this, action);
        }
    }

    private S reduce(A action, S state) {

        return reducer.call(action, state);
    }

    private void notifyStateChanged() {
        for (int i = 0, size = subscribers.size(); i < size; i++) {
            subscribers.get(i).onNotifyChangeState();
        }
    }

    public static void checkState(boolean expression, Object errorMessage) {
        if (!expression) {
            throw new IllegalStateException(String.valueOf(errorMessage));
        }
    }
}